package com.yuandengta.yomall.ware.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.rabbitmq.client.Channel;
import com.yuandengta.common.constant.OrderConstant;
import com.yuandengta.common.to.SkuHasStockTO;
import com.yuandengta.common.to.mq.OrderTO;
import com.yuandengta.common.to.mq.StockDetailTO;
import com.yuandengta.common.to.mq.StockLockedTO;
import com.yuandengta.common.utils.R;
import com.yuandengta.yomall.ware.constant.OrderStatusEnum;
import com.yuandengta.yomall.ware.constant.RabbitConstant;
import com.yuandengta.yomall.ware.constant.StockLockStatusEnum;
import com.yuandengta.yomall.ware.entity.WareOrderTaskDetailEntity;
import com.yuandengta.yomall.ware.entity.WareOrderTaskEntity;
import com.yuandengta.yomall.ware.exception.WareException;
import com.yuandengta.yomall.ware.feign.OrderFeignService;
import com.yuandengta.yomall.ware.feign.ProductFeignService;
import com.yuandengta.yomall.ware.service.WareOrderTaskDetailService;
import com.yuandengta.yomall.ware.service.WareOrderTaskService;
import com.yuandengta.yomall.ware.vo.LockStockResultVO;
import com.yuandengta.yomall.ware.vo.OrderItemVO;
import com.yuandengta.yomall.ware.vo.OrderVO;
import com.yuandengta.yomall.ware.vo.WareSkuLockVO;
import lombok.Data;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yuandengta.common.utils.PageUtils;
import com.yuandengta.common.utils.Query;

import com.yuandengta.yomall.ware.dao.WareSkuDao;
import com.yuandengta.yomall.ware.entity.WareSkuEntity;
import com.yuandengta.yomall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    private WareSkuDao wareSkuDao;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private OrderFeignService orderFeignService;

    @Autowired
    private WareOrderTaskService wareOrderTaskService;

    @Autowired
    private WareOrderTaskDetailService wareOrderTaskDetailService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 查询商品库存
     * @param params
     * @return
     */
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();

        String skuId = (String) params.get("skuId");
        if (!StringUtils.isEmpty(skuId)) {
            queryWrapper.eq("sku_id", skuId);
        }

        String wareId = (String) params.get("wareId");
        if (!StringUtils.isEmpty(wareId)) {
            queryWrapper.eq("ware_id", wareId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    /**
     * 添加库存
     * @param skuId
     * @param wareId
     * @param skuNum
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        List<WareSkuEntity> wareSkuEntities = this.list(
                new QueryWrapper<WareSkuEntity>()
                        .eq("sku_id", skuId)
                        .eq("ware_id", wareId));
        // 新增库存
        if (wareSkuEntities == null || wareSkuEntities.size() == 0) {
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            wareSkuEntity.setWareId(wareId);
            wareSkuEntity.setStock(skuNum);
            wareSkuEntity.setStockLocked(0);

            // TODO 远程查询出sku_name，如果远程调用失败，事务无需回滚
            // 1. 使用 catch 来处理异常
            // 2. TODO 还可以用什么方式来处理出现异常不回滚？
            try {
                R r = productFeignService.info(skuId);
                if (r.getCode() == 0) {
                    Map<String, Object> map = (Map<String, Object>)r.get("skuInfo");
                    wareSkuEntity.setSkuName((String)map.get("skuName"));
                }
            } catch (Exception e) {
                log.error("调用远程服务 yomall-product 查询skuName失败");
            }

            this.save(wareSkuEntity);
        } else {
            // 更新库存
            wareSkuDao.updateStock(skuId, wareId, skuNum);
        }
    }

    /**
     * 查询sku是否有库存
     * @param skuIds
     * @return
     */
    @Override
    public List<SkuHasStockTO> getSkuHasStock(List<Long> skuIds) {
        List<SkuHasStockTO> tos = skuIds.stream().map(skuId -> {
            SkuHasStockTO skuHasStockTO = new SkuHasStockTO();
            skuHasStockTO.setSkuId(skuId);
            //查询当前sku的库存量
            long count = baseMapper.getSkuStock(skuId);
            skuHasStockTO.setHasStock(count > 0);
            return skuHasStockTO;
        }).collect(Collectors.toList());

        return tos;
    }

    /**
     * 为某个订单锁定库存
     *
     * 库存解锁场景：
     *  1.下订单成功，订单过期没有支付被系统自动取消，被用户手动取消
     *  2.下订单成功，库存锁定成功，接下来业务调动失败，导致订单回滚，之前锁定的库存需要自动解锁
     * @param vo
     * @return
     */
    @Transactional(rollbackFor = WareException.class)
    @Override
    public Boolean orderLockStock(WareSkuLockVO vo) {
        // 保存库存工作单
        WareOrderTaskEntity taskEntity = new WareOrderTaskEntity();
        taskEntity.setOrderSn(vo.getOrderSn());
        wareOrderTaskService.save(taskEntity);

        // 常规实现：按照下单的收货地址，找到一个就近仓库，锁定库存
        // 1.这里我们简化逻辑，直接找到每个商品在哪个仓库都有库存
        List<OrderItemVO> locks = vo.getLocks();
        List<SkuWareHasStock> stocks = locks.stream().map(item -> {
            SkuWareHasStock stock = new SkuWareHasStock();
            Long skuId = item.getSkuId();
            stock.setSkuId(skuId);
            stock.setNum(item.getCount());
            List<Long> wareIds = wareSkuDao.listWareIdHasSkuStock(skuId);
            stock.setWareIds(wareIds);
            return stock;
        }).collect(Collectors.toList());

        // 2.锁定库存
        for (SkuWareHasStock stock : stocks) {
            Boolean skuLocked = false;
            Long skuId = stock.getSkuId();
            List<Long> wareIds = stock.getWareIds();
            if (wareIds == null || wareIds.size() == 0){
               throw new WareException("商品id：" + skuId + "库存不足");
            }
            for (Long wareId : wareIds) {
                // 锁定成功返回1，否则就是0
                Long count = wareSkuDao.lockSkuStock(skuId, wareId, stock.getNum());
                if (count == 1) {
                    skuLocked = true;
                    WareOrderTaskDetailEntity taskDetailEntity = new WareOrderTaskDetailEntity();
                    taskDetailEntity.setSkuId(skuId);
                    taskDetailEntity.setSkuNum(stock.getNum());
                    taskDetailEntity.setTaskId(taskEntity.getId());
                    taskDetailEntity.setWareId(wareId);
                    taskDetailEntity.setLockStatus(StockLockStatusEnum.LOCKED.getCode());
                    wareOrderTaskDetailService.save(taskDetailEntity);
                    // 构建消息
                    StockLockedTO lockedTO = new StockLockedTO();
                    lockedTO.setId(taskEntity.getId());
                    StockDetailTO detailTO = new StockDetailTO();
                    BeanUtils.copyProperties(taskDetailEntity, detailTO);
                    lockedTO.setDetailTO(detailTO);
                    // 发送消息
                    rabbitTemplate.convertAndSend(RabbitConstant.STOCK_EVENT_EXCHANGE,
                            RabbitConstant.STOCK_DELAY_QUEUE_ROUTING_KEY, lockedTO);
                    break;
                }
            }
            if (!skuLocked) {
                // 当前商品所有仓库都没有锁定
                throw new WareException("商品id：" + skuId + "库存不足");
            }
        }

        return true;
    }

    /**
     * 自动解锁库存
     * @param lockedTO
     */
    @Override
    public void unLockStock(StockLockedTO lockedTO) {
        // 1.查询数据库关于这个订单的锁定库存信息
        // 有记录：证明库存锁定成功
        //  - 解锁
        //  - 订单情况：
        //  -   1. 没有这个订单，必须解锁
        //  -   2. 有这个订单，根据订单状态，已取消：解锁库存，没有取消：不能解锁
        // 无记录：库存锁定失败，库存回滚了，这种情况无需解锁
        StockDetailTO detailTO = lockedTO.getDetailTO();
        WareOrderTaskDetailEntity taskDetailEntity = wareOrderTaskDetailService.getById(detailTO.getId());
        if (taskDetailEntity != null) {
            // 解锁
            Long id = lockedTO.getId();
            WareOrderTaskEntity taskEntity = wareOrderTaskService.getById(id);
            // 根据订单号查询订单状态
            R r = orderFeignService.getOrderStatus(taskEntity.getOrderSn());
            if (r.getCode() == 0) {
                OrderVO orderVO = r.getData("data", new TypeReference<OrderVO>() {
                });
                // 没有订单，订单取消，都必须解锁库存
                if (orderVO == null || OrderStatusEnum.CANCLED.getCode().equals(orderVO.getStatus())) {
                    // 当前库存工作单的状态为已锁定状态，才需要解锁
                    if (StockLockStatusEnum.LOCKED.getCode().equals(taskDetailEntity.getLockStatus())) {
                        unLockStock(detailTO.getSkuId(), detailTO.getWareId(), detailTO.getSkuNum(), detailTO.getId());
                    }
                }
            } else {
                log.error("调用远程服务 yomall-order 查询订单异常");
                throw new WareException("调用远程服务 yomall-order 查询订单异常");
            }
        }
    }

    /**
     * 解锁库存
     */
    private void unLockStock(Long skuId, Long wareId, Integer num, Long taskDetailId){
        // 更新锁定库存
        wareSkuDao.unLockStock(skuId, wareId, num);
        // 更新库存工作单状态
        WareOrderTaskDetailEntity taskDetailEntity = new WareOrderTaskDetailEntity();
        taskDetailEntity.setId(taskDetailId);
        taskDetailEntity.setLockStatus(StockLockStatusEnum.UNLOCK.getCode());
        wareOrderTaskDetailService.updateById(taskDetailEntity);
    }

    /**
     * 订单关闭解锁库存
     * @param orderTO
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unLockStock(OrderTO orderTO) {
        String orderSn = orderTO.getOrderSn();
        // 查询最新库存状态，防止重复解锁库存
        WareOrderTaskEntity taskEntity = wareOrderTaskService.getOrderTaskByOrderSn(orderSn);
        // 按照库存工作单找到所有没有解锁的库存，进行解锁
        List<WareOrderTaskDetailEntity> detailEntityList = wareOrderTaskDetailService.list(
                new QueryWrapper<WareOrderTaskDetailEntity>()
                        .eq("task_id", taskEntity.getId())
                        .eq("lock_status", StockLockStatusEnum.LOCKED.getCode()));
        for (WareOrderTaskDetailEntity entity : detailEntityList) {
            unLockStock(entity.getSkuId(), entity.getWareId(), entity.getSkuNum(), entity.getId());
        }
    }

    @Data
    class SkuWareHasStock{
        private Long skuId;
        private Integer num;
        private List<Long> wareIds;
    }

}